﻿using System;
using System.Collections.Generic;
using Jayden.Dll.Core.Extensions;
using Jayden.Dll.Web.Enums;
using Jayden.Dll.Web.Exceptions;
using Jayden.Dll.Web.HtmlControls.Descriptions;
using Jayden.Dll.Web.HtmlControls.Generics;
using Jayden.Dll.Web.HtmlControls.Interfaces;

namespace Jayden.Dll.Web.HtmlControls
{
	public class Input : HtmlElement, WithValue, WithName, WithDisabled, WithAutoComplete, WithReadOnly, WithMaxLength, WithSize, WithAutoFocus, WithPlaceholder, WithPatttern, WithRequired, WithChecked, WithAlt, WithSrc, WithList, WithForm, WithMultiple
	{
		public Input() : base(InputDescription.Instance) { }
		public Input(HtmlInputType type, string name, string value)
			: this()
		{
			Type = type;
			Name = name;
			Value = value;
		}

		public string Name { get { return this["name"]; } set { this["name"] = value; } }
		public string Value { get { return this["value"]; } set { this["value"] = value; } }
		public HtmlInputType Type { get { return (HtmlInputType)Enum.Parse(typeof(HtmlInputType), GetAttribute("type", false, false, "text").CamelCase(), true); } set { this["type"] = (value == HtmlInputType.Text) ? null : value.ToString().UnCamelCase(); } }
		public bool Disabled { get { return GetAttributeBool("disabled"); } set { SetAttribute("disabled", value); } }
		public bool Enabled { get { return !Disabled; } set { Disabled = !value; } }
		public bool ReadOnly { get { return GetAttributeBool("readonly"); } set { SetAttribute("readonly", value); } }
		public bool Required { get { return GetAttributeBool("required"); } set { SetAttribute("required", value); } }
		public bool AutoFocus { get { return GetAttributeBool("autofocus"); } set { SetAttribute("autofocus", value); } }
		public bool Multiple { get { return GetAttributeBool("multiple"); } set { SetAttribute("multiple", value); } }

		public bool AutoComplete { get { return GetAttribute("autocomplete", false, true, "on") == "on"; } set { this["autocomplete"] = value ? null : "off"; } }
		public bool Checked { get { return GetAttributeBool("checked"); } set { SetAttribute("checked", value); } }
		public string Placeholder { get { return this["placeholder"]; } set { this["placeholder"] = value; } }
		public string Pattern { get { return this["pattern"]; } set { this["pattern"] = value; } }
		public uint? MaxLength { get { return GetAttributeUint("maxlength"); } set { SetAttribute("maxlength", value); } }
		public uint? Size { get { return GetAttributeUint("size"); } set { SetAttribute("size", value); } }
		public string Src { get { return this["src"]; } set { this["src"] = value; } }
		public string Alt { get { return this["alt"]; } set { this["alt"] = value; } }
		public string List { get { return this["list"]; } set { this["list"] = value; } }
		public string Form { get { return this["form"]; } set { this["form"] = value; } }
		public string DirName { get { return this["dirname"]; } set { this["dirname"] = value; } }

		public string Min { get { return this["min"]; } set { this["min"] = value; } }
		public string Max { get { return this["max"]; } set { this["max"] = value; } }
		public string Step { get { return this["step"]; } set { this["step"] = value; } }

		protected override void TranslateAttributes(Core.Translations.ITranslator translator)
		{
			base.TranslateAttributes(translator);
			Placeholder = translator.Translate(Placeholder);
			Pattern = translator.Translate(Pattern);
			Alt = translator.Translate(Alt);
			// translate button text
			if (Type == HtmlInputType.Submit || Type == HtmlInputType.Button || Type == HtmlInputType.Reset)
				Value = translator.Translate(Value);
		}
	}
	public class InputDescription : HtmlElementDescription
	{
		public static readonly InputDescription Instance = new InputDescription();
		private InputDescription()
			: base("input", HtmlVersion.Html4, false, false)
		{
			m_AttributesByType.Add("text", new HashSet<string>(new string[] { "name", "disabled", "form", "maxlength", "readonly", "size", "value", "autocomplete", "autofocus", "list", "pattern", "required", "placeholder", "dirname" }));
			m_AttributesByType.Add("password", new HashSet<string>(new string[] { "name", "disabled", "form", "maxlength", "readonly", "size", "value", "autocomplete", "autofocus", "pattern", "required", "placeholder" }));
			m_AttributesByType.Add("checkbox", new HashSet<string>(new string[] { "name", "disabled", "form", "checked", "value", "autofocus", "required" }));
			m_AttributesByType.Add("radio", new HashSet<string>(new string[] { "name", "disabled", "form", "checked", "value", "autofocus", "required" }));
			m_AttributesByType.Add("button", new HashSet<string>(new string[] { "name", "disabled", "form", "value", "autofocus" }));
			m_AttributesByType.Add("submit", new HashSet<string>(new string[] { "name", "disabled", "form", "value", "formaction", "autofocus", "formenctype", "formmethod", "formtarget", "formnovalidate" }));
			m_AttributesByType.Add("reset", new HashSet<string>(new string[] { "name", "disabled", "form", "value", "autofocus" }));
			m_AttributesByType.Add("file", new HashSet<string>(new string[] { "name", "disabled", "form", "accept", "autofocus", "required", "multiple" }));
			m_AttributesByType.Add("hidden", new HashSet<string>(new string[] { "name", "disabled", "form", "value" }));
			m_AttributesByType.Add("datetime", new HashSet<string>(new string[] { "name", "disabled", "form", "autocomplete", "autofocus", "list", "max", "min", "step", "readonly", "required", "value" }));
			m_AttributesByType.Add("datetime-local", new HashSet<string>(new string[] { "name", "disabled", "form", "autocomplete", "autofocus", "list", "max", "min", "step", "readonly", "required", "value" }));
			m_AttributesByType.Add("date", new HashSet<string>(new string[] { "name", "disabled", "form", "autocomplete", "autofocus", "list", "max", "min", "step", "readonly", "required", "value" }));
			m_AttributesByType.Add("month", new HashSet<string>(new string[] { "name", "disabled", "form", "autocomplete", "autofocus", "list", "max", "min", "step", "readonly", "required", "value" }));
			m_AttributesByType.Add("time", new HashSet<string>(new string[] { "name", "disabled", "form", "autocomplete", "autofocus", "list", "max", "min", "step", "readonly", "required", "value" }));
			m_AttributesByType.Add("week", new HashSet<string>(new string[] { "name", "disabled", "form", "autocomplete", "autofocus", "list", "max", "min", "step", "readonly", "required", "value" }));
			m_AttributesByType.Add("number", new HashSet<string>(new string[] { "name", "disabled", "form", "autocomplete", "autofocus", "list", "max", "min", "step", "readonly", "required", "placeholder", "value" }));
			m_AttributesByType.Add("range", new HashSet<string>(new string[] { "name", "disabled", "form", "autocomplete", "autofocus", "max", "min", "step", "value" }));
			m_AttributesByType.Add("email", new HashSet<string>(new string[] { "name", "disabled", "form", "autocomplete", "autofocus", "list", "maxlength", "pattern", "readonly", "required", "size", "placeholder", "multiple", "value" }));
			m_AttributesByType.Add("url", new HashSet<string>(new string[] { "name", "disabled", "form", "autocomplete", "autofocus", "list", "maxlength", "pattern", "readonly", "required", "size", "placeholder", "value" }));
			m_AttributesByType.Add("search", new HashSet<string>(new string[] { "name", "disabled", "form", "autocomplete", "autofocus", "list", "maxlength", "pattern", "readonly", "required", "size", "placeholder", "dirname", "value" }));
			m_AttributesByType.Add("tel", new HashSet<string>(new string[] { "name", "disabled", "form", "autocomplete", "autofocus", "list", "maxlength", "pattern", "readonly", "required", "size", "placeholder", "value" }));
			m_AttributesByType.Add("color", new HashSet<string>(new string[] { "name", "disabled", "form", "autocomplete", "autofocus", "list", "value" }));
			m_AttributesByType.Add("image", new HashSet<string>(new string[] { "name", "disabled", "src", "alt", "autofocus", "height", "width" }));

			foreach (KeyValuePair<string, HashSet<string>> pair in m_AttributesByType)
				foreach (string name in pair.Value)
					m_AllAttributes.Add(name);

		}
		public override Type ControlType { get { return typeof(Input); } }
		public override bool IsPhrasingElement { get { return true; } }
		public override string Description { get { return "multipurpose element for representing input controls."; } }

		public override bool CheckValidity(HtmlControl control)
		{
			if (base.CheckValidity(control))
			{
				Input input = control as Input;
				string typeValue = input.Type.ToString().UnCamelCase();
				HashSet<string> attributes = null;
				if (!m_AttributesByType.TryGetValue(typeValue, out attributes))
					throw new HtmlException("<input> with attribute type=\"{0}\" is not allowed.", typeValue);
				foreach(string key in input.Attributes)
					if(key != "type" && !IsGlobalAttribute(key))
						if(!attributes.Contains(key))
							throw new HtmlException("<input> with attribute type=\"{0}\" does not allow attribue with name '{1}'.", typeValue, key);
				return true;
			}
			return false;
		}

		public override IEnumerable<string> PermittedAttributes
		{
			get
			{
				yield return "type";
				foreach (string s in m_AllAttributes)
					yield return s;
			}
		}

		private HashSet<string> m_AllAttributes = new HashSet<string>();
		private Dictionary<string, HashSet<string>> m_AttributesByType = new Dictionary<string, HashSet<string>>();
	}
}
